/* -*-c-*- */

%{
/*
 * scan_vcd.l - scanner for a VCD data file
 *
 * Copyright (C) 2005 Raimund Jacob <raimi@lkcc.org>
 * Copyright (C) 2006, 2007, 2008 Stefan Jahn <stefan@lkcc.org>
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 * 
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this package; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
 * Boston, MA 02110-1301, USA.  
 *
 * $Id$
 *
 */

#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-register"
#endif

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#ifdef __MINGW32__
#include <io.h>
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include "check_vcd.h"
#include "parse_vcd.hpp"

#if !HAVE_STRCHR
# define strchr  index
# define strrchr rindex
#endif

%}

WS       [ \t\n\r]
DIGIT    [0-9]
EXPONENT [Ee][+-]?{DIGIT}+
INT      [+-]?{DIGIT}+
REAL     [+-]?{DIGIT}+("."{DIGIT}+)?{EXPONENT}?
BINARY   [01xXzZ]+
DECIMAL  {DIGIT}+
SCALAR   [01xXzZ]
CODE     [!-~]+
IDENT    [a-zA-Z_][a-zA-Z0-9_.-]*

%x COMMENT SCALE SCOPE IGNORE VAR VAR2 VAR3 VAR4 DUMP TIMESTAMP CHANGE
%x BIN FLOAT

%option yylineno noyywrap nounput noinput prefix="vcd_"

%%

<INITIAL,COMMENT,SCALE,SCOPE,IGNORE,VAR4,DUMP>"$end" {
  BEGIN(INITIAL);
  return t_END;
}

<INITIAL>"$comment" {
  BEGIN(COMMENT);
  return t_COMMENT;
}

<INITIAL>"$date" {
  BEGIN(IGNORE);
  return t_DATE;
}

<INITIAL>"$enddefinitions" {
  return t_ENDDEFINITIONS;
}

<INITIAL>"$scope" {
  BEGIN(SCOPE);
  return t_SCOPE;
}

<INITIAL>"$timescale" {
  BEGIN(SCALE);
  return t_TIMESCALE;
}

<SCALE>"1"   { return ONE; }
<SCALE>"10"  { return TEN; }
<SCALE>"100" { return HUNDRET; }

<SCALE>"s"   { return SECOND; }
<SCALE>"ms"  { return MILLI;  }
<SCALE>"us"  { return MICRO;  }
<SCALE>"ns"  { return NANO;   }
<SCALE>"ps"  { return PICO;   }
<SCALE>"fs"  { return FEMTO;  }

<INITIAL>"$upscope" {
  return t_UPSCOPE;
}

<INITIAL>"$var" {
  BEGIN(VAR);
  return t_VAR;
}

<VAR>"event"     { return EVENT;     }
<VAR>"integer"   { return INTEGER;   }
<VAR>"parameter" { return PARAMETER; }
<VAR>"real"      { return REAL;      }
<VAR>"reg"       { return REG;       }
<VAR>"supply0"   { return SUPPLY0;   }
<VAR>"supply1"   { return SUPPLY1;   }
<VAR>"time"      { return TIME;      }
<VAR>"tri"       { return TRI;       }
<VAR>"triand"    { return TRIAND;    }
<VAR>"trior"     { return TRIOR;     }
<VAR>"trireg"    { return TRIREG;    }
<VAR>"tri0"      { return TRI0;      }
<VAR>"tri1"      { return TRI1;      }
<VAR>"wand"      { return WAND;      }
<VAR>"wire"      { return WIRE;      }
<VAR>"wor"       { return WOR;       }

<INITIAL,DUMP>"1" {
  vcd_lval.value = strdup (vcd_text);
  BEGIN(CHANGE);
  return ONE;
}

<INITIAL,DUMP>"0" {
  vcd_lval.value = strdup (vcd_text);
  BEGIN(CHANGE);
  return ZERO;
}

<INITIAL,DUMP>[xX] {
  vcd_lval.value = strdup ("X");
  BEGIN(CHANGE);
  return X;
}

<INITIAL,DUMP>[zZ] {
  vcd_lval.value = strdup ("Z");
  BEGIN(CHANGE);
  return Z;
}

<TIMESTAMP>{DECIMAL} {
  vcd_lval.real = strtod (vcd_text, NULL);
  BEGIN(INITIAL);
  return PositiveHugeInteger;
}

<INITIAL,DUMP>[rR] {
  BEGIN(FLOAT);
  return 'R';
}

<INITIAL,DUMP>[bB] {
  BEGIN(BIN);
  return 'B';
}

<FLOAT>{REAL}|{INT} {
  vcd_lval.value = strdup (vcd_text);
  BEGIN(CHANGE);
  return Real;
}

<BIN>{BINARY} {
  vcd_lval.value = strdup (vcd_text);
  char * p = vcd_lval.value;
  while (*p) { *p = toupper (*p); p++; }
  BEGIN(CHANGE);
  return Binary;
}

<INITIAL>"$version" {
  BEGIN(IGNORE);
  return t_VERSION;
}

<INITIAL>"$dumpall" {
  return t_DUMPALL;
}

<INITIAL>"$dumpoff" {
  return t_DUMPOFF;
}

<INITIAL>"$dumpon" {
  return t_DUMPON;
}

<INITIAL>"$dumpvars" {
  BEGIN(DUMP);
  return t_DUMPVARS;
}

<SCOPE>"module"   { return s_MODULE;   }
<SCOPE>"task"     { return s_TASK;     }
<SCOPE>"function" { return s_FUNCTION; }
<SCOPE>"fork"     { return s_FORK;     }
<SCOPE>"begin"    { return s_BEGIN;    }

<INITIAL>"#" {
  BEGIN(TIMESTAMP);
  return HASHMARK;
}

<SCOPE>{IDENT} {
  vcd_lval.ident = strdup (vcd_text);
  return Identifier;
}

<VAR>{DECIMAL} {
  vcd_lval.integer = atoi (vcd_text);
  BEGIN(VAR2);
  return PositiveInteger;
}

<VAR2>{CODE} {
  vcd_lval.ident = strdup (vcd_text);
  BEGIN(VAR3);
  return IdentifierCode;
}

<VAR3>{IDENT} {
  vcd_lval.ident = strdup (vcd_text);
  BEGIN(VAR4);
  return Reference;
}

<VAR4>"["   { /* pass the '[' to the parser */ return '['; }
<VAR4>"]"   { /* pass the ']' to the parser */ return ']'; }
<VAR4>":"   { /* pass the ':' to the parser */ return ':'; }
<VAR4>"("   { /* pass the '(' to the parser */ return '('; }
<VAR4>")"   { /* pass the ')' to the parser */ return ')'; }

<VAR4>{DECIMAL} {
  vcd_lval.integer = atoi (vcd_text);
  return PositiveInteger;
}

<CHANGE>{CODE} {
  vcd_lval.ident = strdup (vcd_text);
  BEGIN(INITIAL);
  return IdentifierCode;
}

<IGNORE,COMMENT>. { /* skip any character in here */ }
<*>\r?\n|{WS}     { /* skip end of line and spaces */ }

<*>. { /* any other character is invalid */
  fprintf (stderr, 
	   "line %d: syntax error, unrecognized character: `%s'\n", 
	   vcd_lineno, vcd_text);
  return InvalidCharacter;
}

%%
